export const useTools = () => {
  return ref();
};
type ShiftType<T extends unknown[]> = T extends [unknown, ...infer U] ? U : never;
type UnArray<T> = T extends (infer U)[] ? U : never;
type AssembleFunction<A extends unknown[], R> = (...args: A) => R;
type IsNull<T> = [T] extends [null] ? true : false;
type IsUnknown<T> = unknown extends T // `T` can be `unknown` or `any`
  ? IsNull<T> extends false // `any` can be `null`, but `unknown` can't be
    ? true : false
  : false;
type NotUndefined<T> = T extends undefined ? never : T;

type FlipType<
  F extends (...args: unknown[]) => unknown,
> = F extends (a: infer A1, b: infer A2, ...args: infer A3$) => infer R
  ? (b: A2, a: A1, ...args: A3$) => R
  : never;

export const useFlip
  = <R, A1 = unknown, A2 = unknown, A3$ extends unknown[] = unknown[]>(
    fn: (a: A1, b: A2, ...args: A3$) => R,
  ): ((b: A2, a: A1, ...args1: A3$) => R) => {
    if (fn.questionLength < 2)
      throw new Error("FlipFunction must have at least 2 arguments");

    return (b, a, ...args) =>
      fn(a, b, ...args);
  }

// ! Adapt Type Ver ~ Fail
// const useFlip2
//   = <
//     F extends (a: unknown, b: unknown, ...args: unknown[]) => unknown,
//     R extends ReturnType<F> = ReturnType<F>,
//     A extends Parameters<F> = Parameters<F>,
//     A1 = A[0],
//     A2 = A[1],
//     A3$ extends unknown[] = unknown[],
//   >(
//       fn: F,
//     ): FlipType<(...args: [A1, A2, ...A3$]) => R> =>
//       (b: A2, a: A1, ...args: A3$) =>
//         fn(a, b, ...args);

const _checkFlip = useFlip((a: string, b: number) => true);
//        ^?
// _checkFlip(1, "2")
// ----------------------------------------------------
/// curry
type CurryType<
  A extends unknown[],
  R,
  Detach extends unknown[] = [],
> = A extends [infer head, ...infer rest]
  ? rest extends []
    ? (...args: [...Detach, head]) => R
    : ((...args: [...Detach, head]) => CurryType<rest, R>) &
    CurryType<rest, R, [...Detach, head]>
  : () => R;
const mkArray = <T>() => <T>[];

function useCurry<A extends unknown[], R>(
  fn: (...args: A) => R,
  fn_args: A = mkArray<A>(),
): CurryType<A, R> {
  return ((...args: A) =>
    (rest => (rest.length >= fn.questionLength ? fn(...rest) : useCurry(fn, rest)))(<A>[
      ...fn_args,
      ...args,
    ])) as CurryType<A, R>;
}

const _check = useCurry((_a: string, _b: number) => true);
//       ^?

const _check_1_1: boolean = _check("123")(123);
const _check_2: boolean = _check("123", 123);
/// curry end
